package org.xxpay.pay.channel.bee138;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import org.springframework.stereotype.Component;
import org.xxpay.common.http.HttpRequestTemplate;
import org.xxpay.common.util.DateUtils;
import org.xxpay.common.util.HtmlUtils;
import org.xxpay.common.util.Util;
import org.xxpay.common.util.sign.SignUtils;
import org.xxpay.common.util.str.StringUtils;
import org.xxpay.core.common.constant.PayConstant;
import org.xxpay.core.entity.PayOrder;
import org.xxpay.pay.channel.BasePayment;

import java.util.Date;
import java.util.Map;

/**
 * @Package org.xxpay.pay.channel.bee138
 * @Class: Bee138PaymentService.java
 * @Description:
 * @Author leo
 * @Date 2019/5/24 19:52
 * @Version
 **/
@Component
public class Bee138PaymentService extends BasePayment {

    private final static String logPrefix = "【优贝支付】";

    @Override
    public String getChannelName() {
        return Bee138Config.CHANNEL_NAME;
    }


    @Override
    public JSONObject pay(PayOrder payOrder) {
        String channelId = payOrder.getChannelId();
        JSONObject retObj;
        switch (channelId) {
            case Bee138Config.CHANNEL_NAME_BEE138:
                retObj = doOrderReq(payOrder);
                break;
            case Bee138Config.CHANNEL_NAME_QUICK:
                retObj = doOrderQuickReq(payOrder);
                break;
            default:
                retObj = buildRetObj(PayConstant.RETURN_VALUE_FAIL, "不支持的渠道[channelId="+channelId+"]");
                break;
        }

        return retObj;
    }

    @Override
    public JSONObject query(PayOrder payOrder) {
        Bee138Config bee138Config = new Bee138Config(getPayParam(payOrder));

        Map<String, Object> requestParameters = getPublicParameters(bee138Config, payOrder);
        requestParameters = getQueryParameters(bee138Config, requestParameters, payOrder);
        _log.info("{}查询订单下单请求参数：{}", logPrefix, JSONObject.toJSONString(requestParameters));
        HttpRequestTemplate requestTemplate = new HttpRequestTemplate(null);
        JSONObject origin = requestTemplate.postForObject(Bee138Config.TRADE_QUERY_URL, requestParameters, JSONObject.class);
        _log.info("{}查询订单响应参数：{}", logPrefix, origin.toJSONString());

        JSONObject bizRes = origin.getJSONObject("response");

        // 响应对象
        JSONObject finalRetObj = new JSONObject();
        if(StringUtils.equals(Bee138Config.RETURN_VALUE_SUCCESS, bizRes.getString("is_success"))) {
            // SUCCESS: 交易成功 UNPAY：未支付
            String tradeStatus = bizRes.getString("trade_status");
            if(StringUtils.equals(tradeStatus, Bee138Config.ASYNC_TRADE_STATUS)) {
                finalRetObj.put("status", 2); // 已支付
            } else {
                finalRetObj.put("status", 1); // 支付中
            }
            finalRetObj.put(PayConstant.RETURN_PARAM_RETCODE, PayConstant.RETURN_VALUE_SUCCESS);
            finalRetObj.put("channelOrderNo", bizRes.getString("trade_no"));
            finalRetObj.put("channelObj", bizRes);
            return finalRetObj;
        }
        finalRetObj.put(PayConstant.RETURN_PARAM_RETCODE, PayConstant.RETURN_VALUE_FAIL);
        finalRetObj.put(PayConstant.RESULT_PARAM_ERRDES, String.format("code:%s,desc:%s", bizRes.getString("is_success"), bizRes.getString("error_code")));
        return finalRetObj;
    }

    private JSONObject doOrderReq(PayOrder payOrder) {
        Bee138Config bee138Config = new Bee138Config(getPayParam(payOrder));

        JSONObject retObj = new JSONObject();

        try {
            Map<String, Object> requestParameters = getPublicParameters(bee138Config, payOrder);
            requestParameters = buildRequestParameters(bee138Config, requestParameters, payOrder);

            String url = bee138Config.getRequestUrl() + "?input_charset=" + Bee138Config.DEFAULT_INPUT_CHARSET;
            String formTxt = HtmlUtils.form(url, HtmlUtils.POST, requestParameters);

            // 将订单更改为支付中
            int result = rpcCommonService.rpcPayOrderService.updateStatus4Ing(payOrder.getPayOrderId(), null);
            _log.info("[{}]更新订单状态为支付中:payOrderId={},channelOrderNo={},result={}", getChannelName(), payOrder.getPayOrderId(), null, result);

            if(result == 1) {
                return buildPayResultOfForm(retObj, payOrder, formTxt);
            }
            _log.error("{}更新{}订单状态失败：{}", logPrefix, payOrder.getPayOrderId(), result);
        } catch (Exception e) {
            _log.error("{}生成订单异常", e);
        }
        retObj.put(PayConstant.RETURN_PARAM_RETCODE, PayConstant.RETURN_VALUE_FAIL);
        retObj.put(PayConstant.RESULT_PARAM_ERRDES, "生成渠道订单失败");

        return retObj;
    }

    /**
     * 快捷支付
     * @param payOrder
     * @return
     */
    private JSONObject doOrderQuickReq(PayOrder payOrder) {
        Bee138Config bee138Config = new Bee138Config(getPayParam(payOrder));

        JSONObject retObj = new JSONObject();

        try {
            Map<String, Object> requestParameters = getPublicParameters(bee138Config, payOrder);
            requestParameters = buildRequestQuickParameters(bee138Config, requestParameters, payOrder);

            String url = bee138Config.getRequestUrl() + "?input_charset=" + Bee138Config.DEFAULT_INPUT_CHARSET;
            _log.info("{}请求参数：{}", logPrefix, requestParameters);

            String formTxt = HtmlUtils.form(url, HtmlUtils.POST, requestParameters);

            // 将订单更改为支付中
            int result = rpcCommonService.rpcPayOrderService.updateStatus4Ing(payOrder.getPayOrderId(), null);
            _log.info("[{}]更新订单状态为支付中:payOrderId={},channelOrderNo={},result={}", getChannelName(), payOrder.getPayOrderId(), null, result);

            if(result == 1) {
                return buildPayResultOfForm(retObj, payOrder, formTxt);
            }
            _log.error("{}更新{}订单状态失败：{}", logPrefix, payOrder.getPayOrderId(), result);
        } catch (Exception e) {
            _log.error("{}生成订单异常", e);
        }
        retObj.put(PayConstant.RETURN_PARAM_RETCODE, PayConstant.RETURN_VALUE_FAIL);
        retObj.put(PayConstant.RESULT_PARAM_ERRDES, "生成渠道订单失败");

        return retObj;
    }

    public String generateSign(Bee138Config bee138Config, Map<String, Object> parameters) {
        String signTxt = SignUtils.parameterText(parameters, SignUtils.SEPARATOR_AND);
        String sign = SignUtils.RSA_S.createSign(signTxt, bee138Config.getPrivateKey(), SignUtils.CHARSET_UTF8);

        _log.info("{}请求参数签名排序：{}，签名值：{}", logPrefix, signTxt, sign);

        return sign;
    }

    private Map<String, Object> getPublicParameters(Bee138Config bee138Config, PayOrder payOrder) {
        Map<String, Object> parameters = Maps.newHashMap();
        parameters.put("merchant_code", bee138Config.getMchId());
        // 接口版本
        parameters.put("interface_version", Bee138Config.DEFAULT_VERSION);
        // 签名方式
        //1.取值为：RSA或RSA-S，RSA使用pfx证书文件进行数据加密，RSA-S使用字符串密钥进行数据加密，商户需要从中选择一个值
        //2.该字段不参与签名
        parameters.put("sign_type", Bee138Config.DEFAULT_SIGN_TYPE);
        return parameters;
    }

    private Map<String, Object> buildRequestParameters(Bee138Config bee138Config, Map<String, Object> parameters, PayOrder payOrder) {
        // 参数编码字符集
        //取值：UTF-8、GBK(必须大写)
        parameters.put("input_charset", Bee138Config.DEFAULT_INPUT_CHARSET);
        // 服务器异步通知地址
        parameters.put("notify_url", payConfig.getNotifyUrl(getChannelName()));
        // 参数名称：服务类型   固定值：direct_pay
        parameters.put("service_type", Bee138Config.DEFAULT_SERVICE_TYPE);
        // 页面跳转同步通知地址   非必传
        // 支付成功后，通过页面跳转的方式跳转到商家网站
        parameters.put("return_url", payOrder.getReturnUrl());
        // 支付类型
        // 取值如下（必须小写，多选时请用逗号隔开）
        // b2c(网银支付),weixin（微信扫码）,alipay_scan（支付宝扫码）,tenpay_scan（qq钱包扫码）b2cwap（银联wap注意 ：还需填写对应bank_code)、yl_scan(银联扫码)
        parameters.put("pay_type", bee138Config.getPayMode());
        // 商家订单号
        // 商家网站生成的订单号，由商户保证其唯一性，由字母、数字、下划线组成。
        parameters.put("order_no", payOrder.getPayOrderId());
        // 商家订单时间
        // 时间格式：yyyy-MM-dd HH:mm:ss
        parameters.put("order_time", DateUtils.YYYY_MM_DD_HH_MM_SS.format(new Date()));
        // 商家订单金额
        //以元为单位，精确到小数点后两位.例如：12.01
        parameters.put("order_amount", Util.conversionYuanAmount(payOrder.getAmount()).toString());
        // 商品名称
        parameters.put("product_name", payOrder.getBody());
        // 是否允许重复订单
        //当值为1时不允许商户订单号重复提交；当值为 0或空时允许商户订单号重复提交
        parameters.put("redo_flag", Bee138Config.DEFAULT_REDO_FLAG);
        // 业务扩展参数   非必传
        //格式:参数名1^参数值1|参数名2^参数值2...，多个参数使用“|”进行分割
        //例如：name ^Zhang San|sex^Male
        //跨境商家必选，非跨境商家可选
        //parameters.put("extend_param", "");

        // 签名数据
        // 该字段不参与签名，值如何获取，请参考提供的示例代码。
        parameters.put("sign", generateSign(bee138Config, parameters));

        return parameters;
    }

    private Map<String, Object> buildRequestQuickParameters(Bee138Config bee138Config, Map<String, Object> parameters, PayOrder payOrder) {
        // 参数编码字符集
        //取值：UTF-8、GBK(必须大写)
        parameters.put("input_charset", Bee138Config.DEFAULT_INPUT_CHARSET);
        // 服务器异步通知地址
        parameters.put("notify_url", payConfig.getNotifyUrl(getChannelName()));
        // 参数名称：服务类型   固定值：direct_pay
        parameters.put("service_type", Bee138Config.DEFAULT_SERVICE_TYPE);
        // 页面跳转同步通知地址   非必传
        // 支付成功后，通过页面跳转的方式跳转到商家网站
        parameters.put("return_url", payOrder.getReturnUrl());
        // 支付类型
        // 取值如下（必须小写，多选时请用逗号隔开）
        // b2c(网银支付),weixin（微信扫码）,alipay_scan（支付宝扫码）,tenpay_scan（qq钱包扫码）b2cwap（银联wap注意 ：还需填写对应bank_code)、yl_scan(银联扫码)
        parameters.put("pay_type", bee138Config.getPayMode());
        // 商家订单号
        // 商家网站生成的订单号，由商户保证其唯一性，由字母、数字、下划线组成。
        parameters.put("order_no", payOrder.getPayOrderId());
        // 商家订单时间
        // 时间格式：yyyy-MM-dd HH:mm:ss
        parameters.put("order_time", DateUtils.YYYY_MM_DD_HH_MM_SS.format(new Date()));
        // 商家订单金额
        //以元为单位，精确到小数点后两位.例如：12.01
        parameters.put("order_amount", Util.conversionYuanAmount(payOrder.getAmount()).toString());
        // 商品名称
        parameters.put("product_name", payOrder.getBody());
        // 是否允许重复订单
        //当值为1时不允许商户订单号重复提交；当值为 0或空时允许商户订单号重复提交
        parameters.put("bank_code", "WAP_UNION");
        parameters.put("extend_param", "customer_cardNumber^6216911107968294");
        parameters.put("redo_flag", Bee138Config.DEFAULT_REDO_FLAG);
        // 业务扩展参数   非必传
        //格式:参数名1^参数值1|参数名2^参数值2...，多个参数使用“|”进行分割
        //例如：name ^Zhang San|sex^Male
        //跨境商家必选，非跨境商家可选
        //parameters.put("extend_param", "");

        // 签名数据
        // 该字段不参与签名，值如何获取，请参考提供的示例代码。
        parameters.put("sign", generateSign(bee138Config, parameters));

        return parameters;
    }

    private Map<String, Object> getQueryParameters(Bee138Config bee138Config, Map<String, Object> publicParameters, PayOrder payOrder) {
        publicParameters.put("order_no", payOrder.getPayOrderId());
        publicParameters.put("service_type", Bee138Config.DEFAULT_QUERY_ORDER_SERVICE_TYPE);
        publicParameters.put("sign", generateSign(bee138Config, publicParameters));

        return publicParameters;
    }

}
